package procsim;

public class Multiplexer implements Element{
    private Signal inSig[], cSig[], result;
    private MSignal inSigM, cSigM;
    private Signal enable = new Signal(1);
    protected int inNum;
    private int cNum;
    private int inP=0, cP=0;
    protected boolean enableNotSet = true;
    protected String name = "MP";
    public Multiplexer(int num){
        inNum=Util.roundToNextMultiple(num); // zaokruzivanje na sledeci stepen dvojke
        cNum=(int)Math.getExponent(inNum); // broj kontrolnih signala je ld(inNum)
        inSig = new Signal[inNum];
        cSig = new Signal[cNum];
    }
    public Multiplexer(int number, Signal in[], Signal control[]){
        this(number);
        for(int i=0; i<inNum; i++, inP++){
            if(in[i]!=null) in[i].addElement(this);
            inSig[i]=in[i];
        }
        for(int i=0; i<cNum; i++, cP++){
            if(control[i]!=null) control[i].addElement(this);
            cSig[i]=control[i];
        }
    }
    public Multiplexer(int number, MSignal in, MSignal control){
        this(number);
        if(in!=null) in.addElement(this);
        if(control!=null) control.addElement(this);
        inSigM=in;
        cSigM=control;
    }
    public Multiplexer addIn(Signal sig){
        if(inSigM!=null) // Ne mesamo babe i zabe
            System.err.println("Ne mogu se dodati novi signali");
        else if(inP<inNum){
            if(sig!=null) sig.addElement(this);
            inSig[inP++]=sig;
        }
        else
            System.err.println("Nema vise mesta za nove signale");
        return this;
    }
    public Multiplexer addControl(Signal sig){
        if(cSigM!=null) // Ne mesamo babe i zabe
            System.err.println("Ne mogu se dodati novi signali");
        else if(cP<cNum){
            if(sig!=null) sig.addElement(this);
            cSig[cP++]=sig;
        }
        else
            System.err.println("Nema vise mesta za nove signale");
        return this;
    }
    public Multiplexer setIn(MSignal sig){
        if(sig!=null) sig.addElement(this);
        inSigM=sig;
        return this;
    }
    public Multiplexer setControl(MSignal sig){
        if(sig!=null) sig.addElement(this);
        cSigM=sig;
        return this;
    }
    public Multiplexer setResult(Signal sig){
        result=sig;
        return this;
    }
    public Multiplexer setEnableBit(Signal sig){
        if(enable!=null) enable.addElement(this);
        enable = sig;
        enableNotSet = false;
        return this;
    }
    public void setName(String name){
        this.name = name;
    }
    @Override
    public Multiplexer tick(){
        if(enable!=null && enable.isNull()){
            result.set(0);
            return this;
        }
        Signal[] sigarrayin, sigarrayc;
        if(inSigM==null)
            sigarrayin=inSig;
        else
            sigarrayin=inSigM.getBits();
        if(cSigM==null)
            sigarrayc=cSig;
        else
            sigarrayc=cSigM.getBits();
        if(result!=null) result.set(sigarrayin[Util.binaryArray2Int(Util.signalArray2IntArray(sigarrayc))]);
        return this;
    }
    @Override
    public Signal result(){
        return result;
    }
    @Override
    public String toString(){
        return ""+result();
    }
    @Override
    public void updateSignal(Sig sig) {
        boolean changed = false;
        if(inSigM!=null && inSigM==sig){
            inSigM.set(sig.get(), false);
            changed = true;
        }
        if(cSigM!=null && cSigM==sig){
            cSigM.set(sig.get(), false);
            changed = true;
        }
        if(enable!=null && enable==sig){
            enable.set(sig.get(), false);
            changed = true;
        }
        for(int i=0; i<inSig.length; i++){
            if(inSig[i]!=null && inSig[i]==sig){
                inSig[i].set(sig.get(), false);
                changed = true;
            }
        }
        for(int i=0; i<cSig.length; i++){
            if(cSig[i]!=null && cSig[i]==sig){
                cSig[i].set(sig.get(), false);
                changed = true;
            }
        }
        if(changed) tick();
    }
}
